<?php

namespace CZan\Utils;


class Network
{

    /**
     * 给当前网址增加wid参数
     * @param string $url
     * @param string $wid
     * @param string $sid
     * @return string
     */
    public static function getappwxurl(string $url, string $wid, string $sid = ''): string
    {
        $url = self::add_querystring_var($url, 'wid', $wid);
        if (isset ($sid)) {
            $url = self::add_querystring_var($url, 'sid', $sid);
        }

        return $url;
    }

    /**
     * @param string $url
     * @param string $key
     * @param string $value
     * @return string
     */
    public static function add_querystring_var(string $url, string $key, string $value): string
    {
        $url = preg_replace('/(.*)(?|&)' . $key . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
        $url = substr($url, 0, -1);
        if (strpos($url, '?') === FALSE) {
            return ($url . '?' . $key . '=' . $value);
        } else {
            return ($url . '&' . $key . '=' . $value);
        }
    }

    /**
     * 获取客户端IP地址
     * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
     * @param boolean $adv 是否进行高级模式获取（有可能被伪装）
     * @return mixed
     */
    public static function get_client_ip(int $type = 0, bool $adv = false)
    {
        $type = $type ? 1 : 0;
        static $ip = NULL;
        if ($ip !== NULL) return $ip[$type];
        if ($adv) {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
                $pos = array_search('unknown', $arr);
                if (false !== $pos) unset($arr[$pos]);
                $ip = trim($arr[0]);
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $ip = $_SERVER['HTTP_CLIENT_IP'];
            } elseif (isset($_SERVER['REMOTE_ADDR'])) {
                $ip = $_SERVER['REMOTE_ADDR'];
            }
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        // IP地址合法验证
        $long = sprintf("%u", ip2long($ip));
        $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
        return $ip[$type];
    }

    /**
     * 获取客户端ip
     * @return string
     */
    public static function getClientIp(): string
    {
        global $_SERVER;
        if (getenv('HTTP_CLIENT_IP')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } else if (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } else if (getenv('REMOTE_ADDR')) {
            $ip = getenv('REMOTE_ADDR');
        } else if ($_SERVER['HTTP_X_REAL_IP']) {
            $ip = $_SERVER['HTTP_X_REAL_IP'];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return trim($ip);
    }

    /**
     * 跨域检测
     * @return void
     */
    public static function corsDetect()
    {
        $allowOrigin = ['//m.lnsbigdata.cn', '//xcdsj.lnsbigdata.cn', '//easy.lnsbigdata.cn', "//biz.lnsbigdata.cn", "//mp.lnsbigdata.cn"];
        if (getenv('APP_DEBUG')) {
            header('Access-Control-Allow-Origin: *');

            //$allowOrigin = array_merge($allowOrigin, ["//ln-greentea.ichezone.com", "//local.lngreentea.com","//local.lottery.com","//localhost:8888"]);
        } else {
            //简单请求，只需要设置合理的Access-Control-Allow-Origin即可，如果需要传递cookie或者是其它凭据、自定义头部，则需要处理预请求。
            $origin = $_SERVER['HTTP_ORIGIN'];
            if ($origin) {
                if (!in_array(strtolower(str_replace(['http:', 'https:'], '', $origin)), $allowOrigin)) {
                    header("Status: 404");
                    exit;
                }
                header('Access-Control-Allow-Origin: ' . $origin);
            }
            //pass
        }
    }

    /**
     * 获取当前网址
     * $_SERVER ["HTTP_HOST"] 在一般情况下是等于 $_SERVER ["SERVER_NAME"] ，
     * 但是这两个值的来源是不同的，前者是http协议中Host头部的值，后者是web服务器的配置中ServerName的值
     * 如果端口号不是80，那么HTTP_HOST会带上端口号
     */
    public static function curPageURL(): string
    {
        $pageURL = 'http';

        if (isset($_SERVER ["HTTPS"]) && $_SERVER ["HTTPS"] == "on") {
            $pageURL .= "s";
        }
        $pageURL .= "://";
        $pageURL .= $_SERVER ["HTTP_HOST"] . self::getRequestUri();

        return $pageURL;
    }

    /**
     * POST 请求
     *
     * @param string $url
     * @param array $param
     * @param bool $post_file 是否文件上传
     *
     * @return string content
     */
    public static function http_post(string $url, array $param, bool $post_file = false)
    {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        if ($post_file) {
            $strPOST = $param;
        } else {
            $aPOST = [];
            foreach ($param as $key => $val) {
                $aPOST[] = $key . "=" . urlencode($val);
            }
            $strPOST = join("&", $aPOST);
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($oCurl, CURLOPT_POST, TRUE);
        curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return FALSE;
        }
    }

    /**
     * 爬取网页内容
     * @param array $args
     * @param $callback
     * @return bool|string
     */
    public static function webcurl(array $args, $callback)
    {
        $param = $args;
        if ($callback) {
            $param['callback'] = $callback;
        }
        $url = getenv("WEB_CRAWLER_URL", '');
        $curl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_ENCODING, "");
        curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($curl, CURLOPT_TIMEOUT, 1); // 一秒后断开
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($param));
        curl_setopt($curl, CURLOPT_HTTPHEADER, array(
            "Content-Type: application/json",
            "cache-control: no-cache"
        ));
        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    /**
     * GET 请求
     * @param string $url
     * @return bool|string
     */
    public static function http_curl_get(string $url)
    {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return FALSE;
        }
    }

    /**
     * 系统命令调用方法
     * @param string $project 项目
     * @param array $args 参数
     * @param bool $callback 是否回调
     * @return bool|string
     */
    public static function dogtqurl(string $project, array $args, bool $callback = false, $cmd = NULL)
    {
        $param = array(
            'project' => $project,
            'args' => $args,
            'php_version' => 7
        );
        if ($cmd) {
            $param['cmd'] = $cmd;
        }
        if ($callback) {
            $param['callback'] = $callback;
        }
        $url = config('dogpq.DOGTQ_URL');
        $curl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_ENCODING, "");
        curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($curl, CURLOPT_TIMEOUT, 1); // 一秒后断开
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($param));
        curl_setopt($curl, CURLOPT_HTTPHEADER, array(
            "Content-Type: application/json",
            "cache-control: no-cache"
        ));
        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    /**
     * 获取普通url参数
     * 仅限于&符号分割的那种
     * @param string $url
     * @param string $guid
     * @return array
     */
    public static function convertUrlQuery(string $url, string $guid = '&'): array
    {
        $query = parse_url($url, PHP_URL_QUERY);
        $queryParts = explode($guid, $query);
        $params = array();
        foreach ($queryParts as $param) {
            $item = explode('=', $param);
            $params[$item[0]] = $item[1];
        }
        return $params;
    }

    //异步调用方法，by wxh
    public static function asyn_http($url, $param = [], $retry = FALSE)
    {
        if (preg_replace(['/^http:/', '/^https:/i'], '', $url) == $url) {
            $prot = 'http' . ($_SERVER ["HTTPS"] == "on" ? 's' : '') . '://';
            $host = $prot . $_SERVER['HTTP_HOST'];
            $url = rtrim($host, '/') . '/' . ltrim($url, '/');
        }
        $param['url'] = $url;
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 1); // 一秒后断开
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回

        curl_exec($curl);
        $retry && curl_exec($curl);
        curl_close($curl);
    }

    /**
     * GET 请求
     *
     * @param string $url
     * @return bool|string
     */
    public static function http_get(string $url)
    {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return FALSE;
        }
    }

    private static function iunserializer($notify)
    {
    }

    /**
     * @param int $int
     * @param string $string
     * @return void
     */
    private static function error(int $int, string $string = '')
    {
    }


    /* 将URL中的某参数设为某值*/
    public static function urlSetValue($url, $key, $value): string
    {
        list($url_f, $query) = explode('?', $url);
        parse_str($query, $arr);
        $arr[$key] = $value;

        return $url_f . '?' . http_build_query($arr);
    }

    /**
     * 获取 _SERVER['REQUEST_URI'](url中除域名之外的所有部分) 值的通用解决方案(因为此变量只有apache会提供)
     */
    public static function getRequestUri()
    {
        if (isset($_SERVER['REQUEST_URI'])) {
            $uri = $_SERVER['REQUEST_URI'];
        } else {
            if (isset($_SERVER['argv'])) {
                $uri = $_SERVER['PHP_SELF'] . '?' . $_SERVER['argv'][0];
            } else {
                $uri = $_SERVER['PHP_SELF'] . '?' . $_SERVER['QUERY_STRING'];
            }
        }

        return $uri;
    }

    /**
     * 过滤链接里的参数
     * @param $parm
     * @return bool|mixed|string|string[]|null
     */
    public static function filterparam($parm)
    {
        if (strpos($parm, "(") !== false) {
            return false;
        }

        $parm = str_replace(array('"', "'", "../"), '', $parm);
        $parm = htmlspecialchars($parm);
        $no = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';
        $parm = preg_replace($no, '', $parm);
        $no = '/%0[0-8bcef]/';
        $parm = preg_replace($no, '', $parm);
        $no = '/%1[0-9a-f]/';
        return preg_replace($no, '', $parm);
    }

    /**
     * bsn存储数据
     * @param array $args
     * @param $callback
     * @return bool|string
     */
    public static function bsnapi(array $args, $callback = NULL)
    {
        $param = $args;
        if ($callback) {
            $param['callback'] = $callback;
        }
        $url = getenv('BSN_URL', '');
        $curl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_ENCODING, "");
        curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($curl, CURLOPT_TIMEOUT, 15); // 一秒后断开
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($param));
        curl_setopt($curl, CURLOPT_HTTPHEADER, array(
            "Content-Type: application/json",
            "cache-control: no-cache"
        ));
        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    /**
     * 解码
     * @param $url
     * @return string
     */
    public static function unicode_urldecode($url): string
    {
        preg_match_all('/%u([[:alnum:]]{4})/', $url, $a);

        foreach ($a[1] as $uniord) {
            $dec = hexdec($uniord);

            if ($dec < 128) {
                $utf = chr($dec);
            } else if ($dec < 2048) {
                $utf = chr(192 + (($dec - ($dec % 64)) / 64));
                $utf .= chr(128 + ($dec % 64));
            } else {
                $utf = chr(224 + (($dec - ($dec % 4096)) / 4096));
                $utf .= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64));
                $utf .= chr(128 + ($dec % 64));
            }

            $url = str_replace('%u' . $uniord, $utf, $url);
        }

        return urldecode($url);
    }


    public static function UrlJ($str)
    {
        return str_replace('./', '', str_replace('//', '/', $str));
    }

    /**
     * @param $sk
     * @param $url
     * @param $querystring_arrays
     * @param string $method
     * @return string
     */
    public static function caculateAKSN($sk, $url, $querystring_arrays, string $method = 'GET'): string
    {
        if ($method === 'POST') {
            ksort($querystring_arrays);
        }
        $querystring = http_build_query($querystring_arrays);

        return md5(urlencode($url . '?' . $querystring . $sk));
    }

    /**
     * POST 请求 发送body内容 row
     * @param $url
     * @param $param
     * @param null $need_header
     * @param null $JSON_UNESCAPED_UNICODE
     * @return array|bool|string
     */
    public static function http_post_row($url, $param, $need_header = NULL, $JSON_UNESCAPED_UNICODE = NULL)
    {
        $curl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_ENCODING, "");
        curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");

        if ($JSON_UNESCAPED_UNICODE) {
            $data = json_encode($param, JSON_UNESCAPED_UNICODE);
        } else {
            $data = json_encode($param);
        }

        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_HTTPHEADER, [
            "Content-Type: application/json",
            "cache-control: no-cache"
        ]);
        $response = curl_exec($curl);
        $err = curl_error($curl);
        if ($need_header) {
            // 获得响应结果里的：头大小
            $content_type = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
        }

        curl_close($curl);
        if ($err) {
            return false;
        } else {
            if ($need_header) {
                return ['res' => $response, 'content_type' => $content_type];
            } else {
                return $response;
            }
        }
    }

    /**
     * 修改 https 和 http
     * @param string $url $url 域名
     * @param int $type 0 返回https 1 返回 http
     * @return string
     */
    public static function set_http_type(string $url, int $type = 0): string
    {
        $domainTop = substr($url, 0, 5);
        if ($type) {
            if ($domainTop == 'https') $url = 'http' . substr($url, 5, strlen($url));
        } else {
            if ($domainTop != 'https') $url = 'https:' . substr($url, 5, strlen($url));
        }
        return $url;
    }

    /**
     * @param $url
     * @param array $post
     * @param array $extra
     * @param int $timeout
     * @return array|false|int|resource|string|null
     */
    public static function ihttp_request($url, array $post = [], array $extra = [], int $timeout = 60)
    {
        if (function_exists('curl_init') && function_exists('curl_exec') && $timeout > 0) {

            $fileterDomain = [
                'baidu.com&dataType=json',
                'vip.zhifun.cc',
                'wxapp.zhifun.cc',
                'EWEI_SHOPV2_AUTH_URL'
            ];
            foreach ($fileterDomain as $domain) {
                if (stripos($url, $domain) !== false) {
                    return ['code' => 200, 'status' => 'ok'];
                }
            }

            $ch = self::ihttp_build_curl($url, $post, $extra, $timeout);
            if (is_error($ch)) {
                return $ch;
            }
            $data = curl_exec($ch);
            $errno = curl_errno($ch);
            $error = curl_error($ch);
            curl_close($ch);
            if ($errno || empty($data)) {
                return self::error($errno, $error);
            } else {
                return self::ihttp_response_parse($data);
            }
        }
        $urlset = self::ihttp_parse_url($url, [], true);
        if (!empty($urlset['ip'])) {
            $urlset['host'] = $urlset['ip'];
        }

        $body = self::ihttp_build_httpbody($url, $post, $extra);

        if ($urlset['scheme'] == 'https') {
            $fp = self::ihttp_socketopen('ssl://' . $urlset['host'], $urlset['port'], $errno, $error);
        } else {
            $fp = self::ihttp_socketopen($urlset['host'], $urlset['port'], $errno, $error);
        }
        stream_set_blocking($fp, $timeout > 0);
        stream_set_timeout($fp, ini_get('default_socket_timeout'));
        if (!$fp) {
            return self::error(1, $error);
        } else {
            fwrite($fp, $body);
            $content = '';
            if ($timeout > 0) {
                while (!feof($fp)) {
                    $content .= fgets($fp, 512);
                }
            }
            fclose($fp);
            return self::ihttp_response_parse($content, true);
        }
    }

    /**
     * @param string $url
     * @param array $data
     * @return array|false|int|resource|string|null
     */
    public static function ihttp_get(string $url, array $data = [])
    {
        foreach ($data as $key => $value) {
            if (!strrchr($url, '?')) {
                $pre = '?';
            } else {
                $pre = '&';
            }
            $url .= "$pre$key=$value";
        }
        return self::ihttp_request($url);
    }

    /**
     * @param string $url
     * @param $data
     * @return array|false|int|resource|string|null
     */
    public static function ihttp_json(string $url, $data)
    {
        $headers = ['Content-Type' => 'application/json'];
        return self::ihttp_request($url, $data, $headers);
    }


    public static function ihttp_multi_request($urls, $posts = [], $extra = [], $timeout = 60)
    {
        if (!is_array($urls)) {
            return self::error(1, '请使用ihttp_request函数');
        }
        $curl_multi = curl_multi_init();
        $curl_client = $response = [];

        foreach ($urls as $i => $url) {
            if (isset($posts[$i]) && is_array($posts[$i])) {
                $post = $posts[$i];
            } else {
                $post = $posts;
            }
            if (!empty($url)) {
                $curl = self::ihttp_build_curl($url, $post, $extra, $timeout);
                if (is_error($curl)) {
                    continue;
                }
                if (curl_multi_add_handle($curl_multi, $curl) === CURLM_OK) {
                    $curl_client[] = $curl;
                }
            }
        }
        if (!empty($curl_client)) {
            $active = null;
            do {
                $mrc = curl_multi_exec($curl_multi, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);

            while ($active && $mrc == CURLM_OK) {
                do {
                    $mrc = curl_multi_exec($curl_multi, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }

        foreach ($curl_client as $i => $curl) {
            $response[$i] = curl_multi_getcontent($curl);
            curl_multi_remove_handle($curl_multi, $curl);
        }
        curl_multi_close($curl_multi);
        return $response;
    }

    /**
     * @param $hostname
     * @param int $port
     * @param $errno
     * @param $errstr
     * @param int $timeout
     * @return false|resource|string
     */
    public static function ihttp_socketopen($hostname, int $port = 80, &$errno, &$errstr, int $timeout = 15)
    {
        $fp = '';
        if (function_exists('fsockopen')) {
            $fp = @fsockopen($hostname, $port, $errno, $errstr, $timeout);
        } elseif (function_exists('pfsockopen')) {
            $fp = @pfsockopen($hostname, $port, $errno, $errstr, $timeout);
        } elseif (function_exists('stream_socket_client')) {
            $fp = @stream_socket_client($hostname . ':' . $port, $errno, $errstr, $timeout);
        }
        return $fp;
    }

    /**
     * @param $data
     * @param bool $chunked
     * @return array
     */
    public static function ihttp_response_parse($data, bool $chunked = false): array
    {
        $rlt = [];

        /*	$headermeta = explode('HTTP/', $data);
            if (count($headermeta) > 2) {
                $data = 'HTTP/' . array_pop($headermeta);
            }
            */
        $pos = strpos($data, "\r\n\r\n");
        $split1[0] = substr($data, 0, $pos);
        $split1[1] = substr($data, $pos + 4, strlen($data));

        $split2 = explode("\r\n", $split1[0], 2);
        preg_match('/^(\S+) (\S+) (.*)$/', $split2[0], $matches);

        if ('301' == $matches[2]) {
            $pos = strpos($split1[1], "\r\n\r\n");
            $split1[0] = substr($split1[1], 0, $pos);
            $split1[1] = substr($split1[1], $pos + 4, strlen($split1[1]));
            $split2 = explode("\r\n", $split1[0], 2);
            preg_match('/^(\S+) (\S+) (.*)$/', $split2[0], $matches);
        }

        $rlt['code'] = !empty($matches[2]) ? $matches[2] : 200;
        $rlt['status'] = !empty($matches[3]) ? $matches[3] : 'OK';
        $rlt['responseline'] = !empty($split2[0]) ? $split2[0] : '';
        $header = explode("\r\n", $split2[1]);
        $isgzip = false;
        $ischunk = false;
        foreach ($header as $v) {
            $pos = strpos($v, ':');
            $key = substr($v, 0, $pos);
            $value = trim(substr($v, $pos + 1));
            if (is_array($rlt['headers'][$key])) {
                $rlt['headers'][$key][] = $value;
            } elseif (!empty($rlt['headers'][$key])) {
                $temp = $rlt['headers'][$key];
                unset($rlt['headers'][$key]);
                $rlt['headers'][$key][] = $temp;
                $rlt['headers'][$key][] = $value;
            } else {
                $rlt['headers'][$key] = $value;
            }
            if (!$isgzip && strtolower($key) == 'content-encoding' && strtolower($value) == 'gzip') {
                $isgzip = true;
            }
            if (!$ischunk && strtolower($key) == 'transfer-encoding' && strtolower($value) == 'chunked') {
                $ischunk = true;
            }
        }
        if ($chunked && $ischunk) {
            $rlt['content'] = self::ihttp_response_parse_unchunk($split1[1]);
        } else {
            $rlt['content'] = $split1[1];
        }
        if ($isgzip && function_exists('gzdecode')) {
            $rlt['content'] = gzdecode($rlt['content']);
        }

        $rlt['meta'] = $data;
        if ($rlt['code'] == '100') {
            return self::ihttp_response_parse($rlt['content']);
        }
        return $rlt;
    }

    /**
     * @param $str
     * @return false|string
     */
    public static function ihttp_response_parse_unchunk($str = null)
    {
        if (!is_string($str) or strlen($str) < 1) {
            return false;
        }
        $eol = "\r\n";
        $add = strlen($eol);
        $tmp = $str;
        $str = '';
        do {
            $tmp = ltrim($tmp);
            $pos = strpos($tmp, $eol);
            if ($pos === false) {
                return false;
            }
            $len = hexdec(substr($tmp, 0, $pos));
            if (!is_numeric($len) or $len < 0) {
                return false;
            }
            $str .= substr($tmp, ($pos + $add), $len);
            $tmp = substr($tmp, ($len + $pos + $add));
            $check = trim($tmp);
        } while (!empty($check));
        unset($tmp);
        return $str;
    }


    /**
     * @param string $url
     * @param array $current_url
     * @param bool $set_default_port
     * @return array|false|int|string|void|null
     */
    public static function ihttp_parse_url(string $url, array $current_url, bool $set_default_port = false)
    {
        if (empty($url)) {
            return self::error(1);
        }
        $urlset = parse_url($url);
        if (!empty($urlset['scheme']) && !in_array($urlset['scheme'], ['http', 'https'])) {
            return self::error(1, '只能使用 http 及 https 协议');
        }
        if (empty($urlset['path'])) {
            $urlset['path'] = '/';
        }
        if (!empty($urlset['query'])) {
            $urlset['query'] = "?{$urlset['query']}";
        }
        if (preg_match('/https:\/\//', $url) && !extension_loaded('openssl')) {
            if (!extension_loaded("openssl")) {
                return self::error(1, '请开启您PHP环境的openssl');
            }
        }
        if (empty($urlset['host'])) {
            $urlset['host'] = $current_url['host'];
            $urlset['scheme'] = $current_url['scheme'];
            $urlset['path'] = $current_url['path'] . 'web/' . str_replace('./', '', $urlset['path']);
            $urlset['ip'] = '127.0.0.1';
        } else if (!self::ihttp_allow_host($urlset['host'], [])) {
            return self::error(1, 'host 非法');
        }

        if ($set_default_port && empty($urlset['port'])) {
            $urlset['port'] = $urlset['scheme'] == 'https' ? '443' : '80';
        }
        return $urlset;
    }

    /**
     * @param string $host
     * @param array $ip_white_list
     * @return bool
     */
    public static function ihttp_allow_host(string $host, array $ip_white_list): bool
    {
        if (preg_match('@', $host)) {
            return false;
        }
        $pattern = "/^(10|172|192|127)/";

        if (preg_match($pattern, $host)) {
            if ($ip_white_list && isset($ip_white_list[$host]) && !$ip_white_list[$host]['status']) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param string $url
     * @param array $post
     * @param array $extra
     * @param int $timeout
     * @return array|false|int|resource|string|null
     */
    public static function ihttp_build_curl(string $url, array $post = [], array $extra = [], int $timeout)
    {
        if (!function_exists('curl_init') || !function_exists('curl_exec')) {
            return self::error(1, 'curl扩展未开启');
        }

        $urlset = self::ihttp_parse_url($url, []);

        if (is_error($urlset)) {
            return $urlset;
        }

        if (!empty($urlset['ip'])) {
            $extra['ip'] = $urlset['ip'];
        }

        $ch = curl_init();

        if (!empty($extra['ip'])) {
            $extra['Host'] = $urlset['host'];
            $urlset['host'] = $extra['ip'];
            unset($extra['ip']);
        }
        curl_setopt($ch, CURLOPT_URL, $urlset['scheme'] . '://' . $urlset['host'] . ($urlset['port'] == '80' || empty($urlset['port']) ? '' : ':' . $urlset['port']) . $urlset['path'] . $urlset['query']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
        if ($post) {
            $filepost = false;
            foreach ($post as $name => &$value) {
                if (version_compare(phpversion(), '5.5') >= 0 && is_string($value) && substr($value, 0, 1) == '@') {
                    $post[$name] = new CURLFile(ltrim($value, '@'));
                }
                if ((is_string($value) && substr($value, 0, 1) == '@') || (class_exists('CURLFile') && $value instanceof CURLFile)) {
                    $filepost = true;
                }
            }
            if (!$filepost) {
                $post = http_build_query($post);
            }
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
        }
        if (!empty($GLOBALS['_W']['config']['setting']['proxy'])) {
            $urls = parse_url($GLOBALS['_W']['config']['setting']['proxy']['host']);
            if (!empty($urls['host'])) {
                curl_setopt($ch, CURLOPT_PROXY, "{$urls['host']}:{$urls['port']}");
                $proxytype = 'CURLPROXY_' . strtoupper($urls['scheme']);
                if (!empty($urls['scheme']) && defined($proxytype)) {
                    curl_setopt($ch, CURLOPT_PROXYTYPE, constant($proxytype));
                } else {
                    curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
                    curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
                }
                if (!empty($GLOBALS['_W']['config']['setting']['proxy']['auth'])) {
                    curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['_W']['config']['setting']['proxy']['auth']);
                }
            }
        }
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSLVERSION, 1);
        if (defined('CURL_SSLVERSION_TLSv1')) {
            curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
        }
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1');
        if (!empty($extra) && is_array($extra)) {
            $headers = [];
            unset($value);
            foreach ($extra as $opt => $value) {
                if (strpos($opt, 'CURLOPT_')) {
                    curl_setopt($ch, constant($opt), $value);
                } elseif (is_numeric($opt)) {
                    curl_setopt($ch, $opt, $value);
                } else {
                    $headers[] = "$opt: $value";
                }
            }
            if (!empty($headers)) {
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            }
        }

        return $ch;
    }

    /**
     * @param $url
     * @param $post
     * @param $extra
     * @return array|false|int|string|null
     */
    public static function ihttp_build_httpbody($url, $post, $extra)
    {
        $urlset = self::ihttp_parse_url($url, [], true);
        if (is_error($urlset)) {
            return $urlset;
        }

        if (!empty($urlset['ip'])) {
            $extra['ip'] = $urlset['ip'];
        }

        $body = '';
        if (!empty($post) && is_array($post)) {
            $filepost = false;
            $boundary = random(40);
            foreach ($post as $name => &$value) {
                if ((is_string($value) && substr($value, 0, 1) == '@') && file_exists(ltrim($value, '@'))) {
                    $filepost = true;
                    $file = ltrim($value, '@');

                    $body .= "--$boundary\r\n";
                    $body .= 'Content-Disposition: form-data; name="' . $name . '"; filename="' . basename($file) . '"; Content-Type: application/octet-stream' . "\r\n\r\n";
                    $body .= file_get_contents($file) . "\r\n";
                } else {
                    $body .= "--$boundary\r\n";
                    $body .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
                    $body .= $value . "\r\n";
                }
            }
            if (!$filepost) {
                $body = http_build_query($post);
            } else {
                $body .= "--$boundary\r\n";
            }
        }

        $method = empty($post) ? 'GET' : 'POST';
        $fdata = "$method {$urlset['path']}{$urlset['query']} HTTP/1.1\r\n";
        $fdata .= "Accept: */*\r\n";
        $fdata .= "Accept-Language: zh-cn\r\n";
        if ($method == 'POST') {
            $fdata .= empty($filepost) ? "Content-Type: application/x-www-form-urlencoded\r\n" : "Content-Type: multipart/form-data; boundary=$boundary\r\n";
        }
        $fdata .= "Host: {$urlset['host']}\r\n";
        $fdata .= "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1\r\n";
        if (function_exists('gzdecode')) {
            $fdata .= "Accept-Encoding: gzip, deflate\r\n";
        }
        $fdata .= "Connection: close\r\n";
        if (!empty($extra) && is_array($extra)) {
            unset($value);
            foreach ($extra as $opt => $value) {
                if (!strpos($opt, 'CURLOPT_')) {
                    $fdata .= "$opt: $value\r\n";
                }
            }
        }
        if ($body) {
            $fdata .= 'Content-Length: ' . strlen($body) . "\r\n\r\n$body";
        } else {
            $fdata .= "\r\n";
        }
        return $fdata;
    }

    /**
     * @param string $to
     * @param string $subject
     * @param $body
     * @param bool $global
     * @return bool
     */
    public static function ihttp_email(string $to, string $subject, $body, bool $global = false): bool
    {
        static $mailer;
        set_time_limit(0);

        if (empty($mailer)) {
            if (!class_exists('PHPMailer')) {
                load()->library('phpmailer');
            }
            $mailer = new PHPMailer();
            global $_W;
            $config = $GLOBALS['_W']['setting']['mail'];
            if (!$global) {
                $row = pdo_get("uni_settings", ['uniacid' => $_W['uniacid']], ['notify']);
                $row['notify'] = @self::iunserializer($row['notify']);
                if (!empty($row['notify']) && !empty($row['notify']['mail'])) {
                    $config = $row['notify']['mail'];
                }
            }

            $config['charset'] = 'utf-8';
            if ($config['smtp']['type'] == '163') {
                $config['smtp']['server'] = 'smtp.163.com';
                $config['smtp']['port'] = 25;
            } elseif ($config['smtp']['type'] == 'qq') {
                $config['smtp']['server'] = 'ssl://smtp.qq.com';
                $config['smtp']['port'] = 465;
            } else {
                if (!empty($config['smtp']['authmode'])) {
                    $config['smtp']['server'] = 'ssl://' . $config['smtp']['server'];
                }
            }

            if (!empty($config['smtp']['authmode'])) {
                if (!extension_loaded('openssl')) {
                    return self::error(1, '请开启 php_openssl 扩展！');
                }
            }
            $mailer->signature = $config['signature'];
            $mailer->isSMTP();
            $mailer->CharSet = $config['charset'];
            $mailer->Host = $config['smtp']['server'];
            $mailer->Port = $config['smtp']['port'];
            $mailer->SMTPAuth = true;
            $mailer->Username = $config['username'];
            $mailer->Password = $config['password'];
            !empty($config['smtp']['authmode']) && $mailer->SMTPSecure = 'ssl';

            $mailer->From = $config['username'];
            $mailer->FromName = $config['sender'];
            $mailer->isHTML(true);
        }
        if ($body) {
            if (is_array($body)) {
                $newbody = '';
                foreach ($body as $value) {
                    if (substr($value, 0, 1) == '@') {
                        if (!is_file($file = ltrim($value, '@'))) {
                            return self::error(1, $file . ' 附件不存在或非文件！');
                        }
                        $mailer->addAttachment($file);
                    } else {
                        $newbody .= $value . '\n';
                    }
                }
                $body = $newbody;
            } else {
                if (substr($body, 0, 1) == '@') {
                    $mailer->addAttachment(ltrim($body, '@'));
                    $body = '';
                }
            }
        }
        if (!empty($mailer->signature)) {
            $body .= htmlspecialchars_decode($mailer->signature);
        }
        $mailer->Subject = $subject;
        $mailer->Body = $body;
        $mailer->addAddress($to);
        if ($mailer->send()) {
            return true;
        } else {
            return self::error(1, $mailer->ErrorInfo);
        }
    }

}
